﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Media;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;

namespace AntiAddictionAides
{
    public class ProcessMonitor : IDisposable
    {
        private readonly Timer _timer;
        private readonly Dictionary<string, MonitorItem> _monitorItems = new Dictionary<string, MonitorItem>();
        private readonly object _lock = new object();
        private bool _disposed;

        public ProcessMonitor()
        {
            _timer = new Timer(CheckProcesses, null, Timeout.Infinite, Timeout.Infinite);
        }

        public void Start()
        {
            // 每30秒检查一次
            _timer.Change(0, 5000);
        }

        public void Stop()
        {
            _timer.Change(Timeout.Infinite, Timeout.Infinite);
        }

        public MonitorItem SetMonitor(string processNamePattern, TimeSpan threshold)
        {
            var regex = ConvertWildcardToRegex(processNamePattern);
            var monitor = new MonitorItem(regex, threshold);
            lock (_lock)
            {
                _monitorItems[processNamePattern] = monitor;
            }
            AppForm.Log($"添加监控，进程：{processNamePattern} 沉迷时间:{threshold.TotalSeconds} 秒");
            return monitor;
        }

        public void RemoveMonitor(string processNamePattern)
        {
            lock (_lock)
            {
                if (_monitorItems.TryGetValue(processNamePattern, out var item))
                {
                    item.Dispose();
                    _monitorItems.Remove(processNamePattern);
                }
            }
            AppForm.Log($"删除监控，进程：{processNamePattern}");
        }

        private void CheckProcesses(object state)
        {
            lock (_lock)
            {
                foreach (var item in _monitorItems.Values)
                {
                    AppForm.Log($"任务检查，进程：{item.ProcessNameRegex}");
                    foreach (var process in Process.GetProcesses())
                    {
                        try
                        {
                            if (!item.ProcessNameRegex.IsMatch(process.ProcessName))
                            {
                                continue;
                            }
                            item.Alert(process);
                        }
                        catch (Exception ex) when (ex is Win32Exception || ex is InvalidOperationException)
                        {
                            // 处理进程访问异常
                        }
                        finally
                        {
                            process.Dispose();
                        }
                    }
                }
            }
        }

        private Regex ConvertWildcardToRegex(string pattern)
        {
            return new Regex("^" +
                Regex.Escape(pattern).Replace("\\*", ".*").Replace("\\?", ".") +
                "$", RegexOptions.IgnoreCase);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    Stop();
                    foreach (var item in _monitorItems.Values)
                    {
                        item.Dispose();
                    }
                    _timer.Dispose();
                }
                _disposed = true;
            }
        }

        ~ProcessMonitor()
        {
            Dispose(false);
        }

        public class MonitorItem
        {
            public Dictionary<int, AlertContext> ProcessAlert { get; set; }
            public Regex ProcessNameRegex { get; }
            public TimeSpan Threshold { get; }
            public List<IAlertStrategy> Alerts { get; private set; }

            public MonitorItem(Regex regex, TimeSpan threshold, IAlertStrategy[] alerts = null)
            {
                ProcessNameRegex = regex;
                Threshold = threshold;
                Alerts = new List<IAlertStrategy>();
                if (alerts != null)
                {
                    Alerts.AddRange(alerts);
                }
                ProcessAlert = new Dictionary<int, AlertContext>();
            }

            AlertContext GetAlertContext(Process process, int pid=0, string pname=null)
            {
                AlertContext context;
                lock (ProcessAlert)
                {
                    if (!ProcessAlert.TryGetValue(process.Id, out context) || process.ProcessName != context.ProcessName)
                    {
                        if(pid > 0 && !string.IsNullOrWhiteSpace(pname))
                        {
                            context = new AlertContext()
                            {
                                ProcessId = pid,
                                ProcessName = pname,
                                LastPlayed = DateTime.Now
                            };
                            ProcessAlert[pid] = context;
                        }
                    }
                }
                return context;
            }


            public void Alert(Process process)
            {
                var pinfo = $"[{process.Id}-{process.ProcessName}]";
                AlertContext context = GetAlertContext(process, process.Id, process.ProcessName);
                if (context == null)
                {
                    AppForm.Log($"获取告警上下文失败:{pinfo}");
                    return;
                }
                AppForm.Log($"获取{pinfo}上下文信息，告警数:{context.AlertNum} 最近时间:{context.LastPlayed}");
                if (context.AlertNum > 3)
                {
                    AppForm.Log($"进程{pinfo}告警数{context.AlertNum}超限，已忽略");
                    return;
                }
                context.RunTime = DateTime.Now - process.StartTime;
                if (context.RunTime < Threshold)
                {
                    AppForm.Log($"进程{pinfo}未达沉迷时限，已忽略");
                    return;
                }
                if (DateTime.Now - context.LastPlayed < TimeSpan.FromSeconds(10))
                {
                    AppForm.Log($"进程{pinfo}仍在提醒期限内，已忽略");
                    return;
                }

                context.AlertNum++;
                context.LastPlayed = DateTime.Now;
                AppForm.Log($"进程{pinfo}在{context.LastPlayed}触发提醒{context.AlertNum}");

                // 默认提示音
                if (Alerts.Count == 0)
                {
                    new SoundAlertStrategy("Alarm01.wav").ExecuteAsync(context);
                }
                else
                {
                    foreach(var v in Alerts)
                    {
                        v.ExecuteAsync(context);
                    }
                }
            }

            public int AddAlert(IAlertStrategy alert)
            {
                int idx = 0;
                lock(Alerts)
                {
                    Alerts.Add(alert);
                    idx = Alerts.Count;
                }
                return idx;
            }

            public void RemoveAlert(int idx)
            {
                if(idx < 0 || idx >= Alerts.Count)
                {
                    return;
                }

                lock (Alerts)
                {
                    Alerts.RemoveAt(idx);
                }
            }

            public void Dispose()
            {
                foreach(var v in Alerts)
                {
                    if(v is SoundAlertStrategy sa)
                    {
                        sa.Dispose();
                    }
                }
            }
        }
    }

    public class WinMediaWav
    {
        public static readonly string MediaPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "Media");
        public static string[] Wavs
        {
            get
            {
                return null;
            }
        }

        public static string Wav(string name)
        {
            var file = Path.Combine(MediaPath, name);
            var files = new string[] { name, file, file + ".wav" };
            foreach(var v in files)
            {
                if (File.Exists(v))
                {
                    return v;
                }
            }
            return null;
        }
    }


    public interface IAlertStrategy
    {
        Task ExecuteAsync(AlertContext context);
    }

    public class SoundAlertStrategy : IAlertStrategy
    {
        private readonly SoundPlayer _player;

        public SoundAlertStrategy(string soundFilePath)
        {
            _player = new SoundPlayer(WinMediaWav.Wav(soundFilePath));
        }

        public Task ExecuteAsync(AlertContext context)
        {
            AppForm.Log($"执行提示音{context.RunTime} {context.ProcessId}-{context.ProcessName}");
            return Task.Run(() => _player.PlaySync());
        }

        public void Dispose()
        {
            _player.Dispose();
        }
    }

    public class MessageBoxAlertStrategy : IAlertStrategy
    {
        private string Text { get; set; }
        public MessageBoxAlertStrategy(string text)
        {
            Text = text;
        }

        public Task ExecuteAsync(AlertContext context)
        {
            return Task.Run(() =>
            {
                AppForm.Log($"执行弹框提醒{context.RunTime} {context.ProcessId}-{context.ProcessName}");
                var result = System.Windows.Forms.MessageBox.Show(
                    Text ?? $"老铁，醒醒，该休息啦！",
                    $"防沉迷提醒-{context.ProcessName} 沉迷时间:{context.RunTime}",
                    System.Windows.Forms.MessageBoxButtons.OK,
                    System.Windows.Forms.MessageBoxIcon.Information);
            });
        }
    }

    public class WindowShakeStrategy : IAlertStrategy
    {
        private readonly IntPtr _windowHandle;

        public WindowShakeStrategy(IntPtr hWnd)
        {
            _windowHandle = hWnd;
        }

        public async Task ExecuteAsync(AlertContext context)
        {
            await Task.Run(() =>
            {
                AppForm.Log($"执行窗口抖动提醒{context.RunTime} {context.ProcessId}-{context.ProcessName}");
                for (int i = 0; i < 3; i++)
                {
                    NativeMethods.ShakeWindow(_windowHandle);
                    Thread.Sleep(200);
                }
            });
        }
    }

    public class AlertContext
    {
        public int ProcessId { get; set; }
        public string ProcessName { get; set; }
        public TimeSpan RunTime { get; set; }
        public DateTime LastPlayed { get; set; } = DateTime.MinValue;
        public int AlertNum { get; set; }
    }

    internal static class NativeMethods
    {
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);

        [System.Runtime.InteropServices.DllImport("user32.dll")]
        private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

        [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
        private struct RECT
        {
            public int Left;
            public int Top;
            public int Right;
            public int Bottom;
        }

        public static void ShakeWindow(IntPtr hWnd)
        {
            const uint SWP_NOSIZE = 0x0001;
            const uint SWP_NOZORDER = 0x0004;
            const int SHAKE_DISTANCE = 10; // 抖动幅度
            const int SHAKE_SPEED = 20;    // 抖动速度（毫秒）

            // 获取原始窗口位置
            if (!GetWindowRect(hWnd, out RECT originalRect))
                return;

            int originalX = originalRect.Left;
            int originalY = originalRect.Top;

            // 抖动动画
            for (int i = 0; i < 3; i++)
            {
                // 右移
                SetWindowPos(hWnd, IntPtr.Zero,
                    originalX + SHAKE_DISTANCE, originalY,
                    0, 0, SWP_NOSIZE | SWP_NOZORDER);
                System.Threading.Thread.Sleep(SHAKE_SPEED);

                // 左移
                SetWindowPos(hWnd, IntPtr.Zero,
                    originalX - SHAKE_DISTANCE, originalY,
                    0, 0, SWP_NOSIZE | SWP_NOZORDER);
                System.Threading.Thread.Sleep(SHAKE_SPEED);
            }

            // 恢复原始位置
            SetWindowPos(hWnd, IntPtr.Zero,
                originalX, originalY,
                0, 0, SWP_NOSIZE | SWP_NOZORDER);
        }
    }
}
